﻿#if UNITY_2019_4_OR_NEWER
#define UNITY_3D
#endif

#if UNITY_3D && (UNITY_EDITOR || COMMAND_PROFILER)
#define PROFILING
#endif

using System.Collections;
using System.Collections.Generic;
using System.Threading;

#if UNITY_3D
using UnityEngine;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif

#if PROFILING
using UnityEngine.Profiling;
#endif

namespace Devil
{
    using IteratorStack = Stack<IEnumerator>;
  
    public interface IThreadWorker { }

    internal interface ILoopWorker
    {
        float deltaTime { get; }
    }

    public interface ICommandHandler
    {
        ParallelDispatcher.Handler GetHandler();
    }

    public partial class ParallelDispatcher : System.IDisposable
    {
        //const float tick_2_millis = 0.0001f; // 1f / 10000f;
        // yield command stack size
#if UNITY_EDITOR
        static bool sPause;
        public static bool IsPaused => sPause;
#endif


        const int ITERATOR_STACK_SIZE = 8;
        public const double tick_2_seconds = 0.0000001d;
        public const int TICK_MILLIS = 30;
        //static int sProcessorCount = 16;
        static ParallelDispatcher sSchedule;
        
        readonly static object sync_lock = new object();

        static System.Random sRandom;
        public static float GetRandomValue()
        {
            lock (sync_lock)
            {
                if (sRandom == null)
                    sRandom = new System.Random(ParallelUtils.GenUniqueID());
            }
            return (float)sRandom.NextDouble();
        }

        public static int GetRandomValue(int min, int max)
        {
            lock (sync_lock)
            {
                if (sRandom == null)
                    sRandom = new System.Random(ParallelUtils.GenUniqueID());
            }
            return sRandom.Next(min, max);
        }

        public static float GetRandomValue(float min, float max)
        {
            lock (sync_lock)
            {
                if (sRandom == null)
                    sRandom = new System.Random(ParallelUtils.GenUniqueID());
            }
            var v = sRandom.NextDouble();
            return (max - min) * (float)v + min;
        }

        public static ParallelDispatcher GetInstance()
        {
            lock (sync_lock)
            {
                return sSchedule;
            }
        }


        /// <summary>
        /// 初始化子线程数据(应该确保该方法在主线程执行)。
        /// </summary>
        public static void InitApplicationData()
        {
            //Initialize();

            lock (sync_lock)
            {
                if (/*!sIsInitialized ||*/ sSchedule == null || sSchedule.mDisposed)
                {
                    sSchedule = null;

#if THREAD_WORKERS_LIMITED
                    var processor = 4;
#elif UNITY_3D
                    var processor = SystemInfo.processorCount;
#else
                    // not unity 3d
                    var processor = System.Environment.ProcessorCount;
#endif
                    var processors = ParallelUtils.Max(8, processor - 1);
                    /*sSchedule = */
                    new ParallelDispatcher(128, processors);
                    //sStackPool = new ObjectPool<IteratorStack>(64, () => new IteratorStack(ITERATOR_STACK_SIZE));
                }
            }
        }

        public static void QuitApplication()
        {
            ParallelDispatcher ct = null;
            lock (sync_lock)
            {
                ct = sSchedule;
                sSchedule = null;
            }
            if (ct != null)
            {
                ct.Dispose();
            }
        }

        static MainWorker GetMainWorker()
        {
            MainWorker worker = null;
            lock(sync_lock)
            {
                if (sSchedule != null)
                    worker = sSchedule.mMainThreadWorker;
            }
            return worker;
        }

#if UNITY_EDITOR
        public static void SetPause(bool pause)
        {
            if (pause != sPause)
            {
                sPause = pause;
                Debug.LogFormat("[Parallel] App was {0}", pause ? "Paused" : "Resumed");
            }
        }
#endif

        // 主线程执行逻辑
        public static void MainUpdate(float deltaTime)
        {
#if UNITY_EDITOR
            if(sPause)
                return;
#endif
            var worker = GetMainWorker();
            if (worker != null)
                worker.Update(deltaTime);
        }

        int mIdCtor;
        readonly object mLock = new object();

        ObjectPool<IteratorStack> mStackPool;
        // standy commands
        CascadeList<BaseCommand> mCommands;
        // execute commands
        CascadeList<BaseCommand> mMainCmdQueue;
        // sub execute index
        int mExecIndex;
        // sub executes
        CascadeList<BaseCommand> mSubCmdQueue;
        ThreadFlag mDispatchingState;
        HashSet<int> mRecursiveCheck;
        Stack<BaseCommand> mDependentCheck;
        ThreadWorker[] mWorkers;
        MainWorker mMainThreadWorker;
        LoopWorker mLooper;
        int mActiveWorkers;
        bool mDisposed;
        int mMainThreadID;

        int NextID()
        {
            var id = ++mIdCtor;
            if (id == 0)
                id = ++mIdCtor;
            return id;
        }

        public static bool IsMainThread
        {
            get
            {
                var tid = Thread.CurrentThread.ManagedThreadId;
                lock (sync_lock)
                {
                    if (sSchedule != null)
                        return tid == sSchedule.mMainThreadID;
                }
                return false;
            }
        }

        // 确保在主线程构造该对象
        private ParallelDispatcher(int initComands, int initThreadWorkers)
        {
            mMainThreadID = Thread.CurrentThread.ManagedThreadId;
            EquatableDelegate<BaseCommand> eqCmd = (a, b) => a.id == b.id;
            EquatableDelegate<ILoopCommand> eqLooper = (a, b) => a == null ? (b == null) : a.Equals(b);
            mStackPool = new ObjectPool<IteratorStack>(64, () => new IteratorStack(ITERATOR_STACK_SIZE));
            mCommands = new CascadeList<BaseCommand>(initComands, eqCmd);// new RandomList<BaseCommand>(initComands, eqCmd);
            mSubCmdQueue = new CascadeList<BaseCommand>(initComands, eqCmd);
            mMainCmdQueue = new CascadeList<BaseCommand>(initComands, eqCmd);
            //mDispatcher = DispatchCommands;
            mRecursiveCheck = new HashSet<int>();
            mDependentCheck = new Stack<BaseCommand>(8);
            //mScheduleBuffer = new RandomList<CommandSchedule>(initComands, (a, b) => a.cmd.id == b.cmd.id);

            mMainThreadWorker = new MainWorker(this, 64);
            mLooper = new LoopWorker(this);
            mWorkers = new ThreadWorker[initThreadWorkers];
            for (int i = 0; i < initThreadWorkers; i++)
            {
                mWorkers[i] = new ThreadWorker(this, i);
            }

            mDispatchingState = new ThreadFlag();
            lock (sync_lock)
            {
                if (sSchedule != null)
                    sSchedule.Dispose();
                sSchedule = this;
            }
            Debug.LogFormat("[Parallel] ParallelDispatcher was initialized with {0} thread workers.", initThreadWorkers);
            mDispatchingState.Start(DispatchCommands);

        }

        public Handler Schedule(ICommand command, bool mainThread, Handler dependency)
        {
            if (command == null)
                throw new System.NullReferenceException("command is null.");
            lock (mLock)
            {
                var id = NextID();
                BaseCommand exec = new SubCmd(id, mainThread, command, dependency.cmd);
                mCommands.Add(exec);
                return new Handler(this, exec);
            }
        }

        public Handler Schedule(IYieldCommand command, bool mainThread, Handler dependency)
        {
            if (command == null)
                throw new System.NullReferenceException("command is null.");
            lock (mLock)
            {
                var id = NextID();
                BaseCommand exec = new YieldCmd(id, command, mainThread, dependency.cmd);
                mCommands.Add(exec);
                return new Handler(this, exec);
            }
        }

        public Handler Schedule(ILoopCommand command, bool mainThread, Handler dependency)
        {
            if (command == null)
                throw new System.NullReferenceException("command is null.");
            lock (mLock)
            {
                var id = NextID();
                BaseCommand exec = new LoopCmd(id, command, mainThread, dependency.cmd);
                mCommands.Add(exec);
                return new Handler(this, exec);
            }
        }

        public Handler Schedule<T>(ICommandParallelFor<T> command, IEnumerable enumerable, Handler dependency)
        {
            if (command == null)
                throw new System.NullReferenceException("command is null.");
            if (enumerable == null)
                return default;
            lock (mLock)
            {
                var id = NextID();
                BaseCommand exec = new ParallelCmd<T>(id, command, enumerable, dependency.cmd);
                mCommands.Add(exec);
                return new Handler(this, exec);
            }
        }

        public Handler Schedule(ICommandParallelFor<int> command, int count, Handler dependency)
        {
            if (command == null)
                throw new System.NullReferenceException("command is null.");
            if (count <= 0)
                return default;
            lock (mLock)
            {
                var id = NextID();
                BaseCommand exec = new ParllelIndex(id, count, command, dependency.cmd);
                mCommands.Add(exec);
                return new Handler(this, exec);
            }
        }

        bool IsDependentOn(BaseCommand basecmd, BaseCommand dependent)
        {
            if (basecmd == dependent || basecmd.dependentOn == null)
                return false;
            mDependentCheck.Clear();
            mRecursiveCheck.Clear();
            mRecursiveCheck.Add(basecmd.id);
            mDependentCheck.Push(basecmd);
            while (mDependentCheck.Count > 0)
            {
                var cmd = mDependentCheck.Pop();
                var next = cmd.dependentOn;
                if (next == dependent)
                    return true;
                if (next.dependentOn != null && mRecursiveCheck.Add(next.id))
                {
                    mDependentCheck.Push(next);
                }
                var wait = cmd as WaitCmd;
                if (wait != null && wait.mWaitTargets != null)
                {
                    for (int i = wait.mWaitTargets.Count - 1; i >= 0; i--)
                    {
                        next = wait.mWaitTargets[i];
                        if (next == dependent)
                            return true;
                        if (next.dependentOn != null && mRecursiveCheck.Add(next.id))
                            mDependentCheck.Push(next);
                    }
                }
            }
            mRecursiveCheck.Clear();
            return false;
        }

        void GetDependentExecuteProgress(BaseCommand baseCmd, ref float progress, ref int cmdNum)
        {
            mDependentCheck.Clear();
            mRecursiveCheck.Clear();
            mRecursiveCheck.Add(baseCmd.id);
            mDependentCheck.Push(baseCmd);
            while (mDependentCheck.Count > 0)
            {
                var cmd = mDependentCheck.Pop();
                var next = cmd.dependentOn;
                if (next != null && mRecursiveCheck.Add(next.id))
                    mDependentCheck.Push(next);
                var wait = cmd as WaitCmd;
                if (wait == null)
                {
                    progress += cmd.GetExecutionProgress(this);
                    cmdNum++;
                }
                else if (wait.mWaitTargets != null)
                {
                    lock (wait)
                    {
                        for (int i = wait.mWaitTargets.Count - 1; i >= 0; i--)
                        {
                            next = wait.mWaitTargets[i];
                            if (mRecursiveCheck.Add(next.id))
                                mDependentCheck.Push(next);
                        }
                    }
                }
            }
            mRecursiveCheck.Clear();
        }

        internal void Abort(BaseCommand cmd, bool includeDependents)
        {
            if (cmd == null || mDisposed)
                return;
            lock (cmd)
            {
                if (cmd.isComplete)
                    return;
                cmd.isComplete = true;
            }
            if (includeDependents && cmd.dependentOn != null && !cmd.dependentOn.isComplete)
            {
                var lst = new List<BaseCommand>();
                lst.Add(cmd);
                lock (mLock)
                {
                    mDependentCheck.Clear();
                    mDependentCheck.Push(cmd);
                    while (mDependentCheck.Count > 0)
                    {
                        var it = mDependentCheck.Pop();
                        //mCommands.Remove(it);
                        var dependent = it.dependentOn;
                        if (dependent != null)
                        {
                            bool push = false;
                            lock (dependent)
                            {
                                if (!dependent.isComplete)
                                {
                                    push = true;
                                    dependent.isComplete = true;
                                }
                            }
                            if (push)
                            {
                                mDependentCheck.Push(dependent);
                                mCommands.Remove(dependent);
                                lst.Add(dependent);

                            }
                        }
                        var wait = it as WaitCmd;
                        if (wait != null && wait.mWaitTargets != null)
                        {
                            lock (wait)
                            {
                                for (int i = wait.mWaitTargets.Count - 1; i >= 0; i--)
                                {
                                    it = wait.mWaitTargets[i];
                                    if (it == null)
                                        continue;
                                    var push = false;
                                    lock (it)
                                    {
                                        if (!it.isComplete)
                                        {
                                            it.isComplete = true;
                                            push = true;
                                        }
                                    }
                                    if (push)
                                    {
                                        mDependentCheck.Push(it);
                                        mCommands.Remove(it);
                                        lst.Add(it);
                                    }
                                }
                            }
                        }
                    }
                }
                for (int i = 0; i < lst.Count; i++)
                {
                    lst[i].OnBreak();
                }
            }
            else
            {
                lock (mLock)
                {
                    mCommands.Remove(cmd);
                }
                cmd.OnBreak();
            }
        }

        // 任务分配线程
        void DispatchCommands(object state)
        {
            var threadid = Thread.CurrentThread.ManagedThreadId;
#if UNITY_EDITOR
            //RTLog.LogFormat(LogCat.Editor, "Start Dispatch Command @Thread[{0}]", threadid);
#endif
            var flag = state as ThreadFlag;
            int dispatchIndex = 0;
            while (flag != null && !flag.IsAbort)
            {
                BaseCommand exec = null;

#if UNITY_EDITOR
                if (sPause)
                {
                    Thread.Sleep(TICK_MILLIS);
                    continue;
                }
#endif
                lock (mLock)
                {
                    if (dispatchIndex >= mCommands.Count)
                        dispatchIndex = 0;
                    while (dispatchIndex < mCommands.Count)
                    {
                        var cmd = mCommands[dispatchIndex];
                        if (!cmd.IsReady)
                        {
                            dispatchIndex++;
                            continue;
                        }
                        mCommands.RemoveAt(dispatchIndex++);
                        exec = cmd;
                        break;
                    }
                }
                if (exec != null && !exec.isComplete)
                {
                    exec.Schedule(this);
                }
                else
                {
                    Thread.Sleep(TICK_MILLIS);
                }
            }
#if UNITY_EDITOR
            //RTLog.LogFormat(LogCat.Editor, "Dispatch Command @Thread[{0}] Stoped.", threadid);
#endif
        }

        public void Dispose()
        {
            lock (mLock)
            {
                if (mDisposed)
                    return;
                mDisposed = true;
            }
            if (mDispatchingState != null)
                mDispatchingState.Abort();
            mDispatchingState = null;
            if (mLooper.threadFlag != null)
                mLooper.threadFlag.Abort();
            for (int i = mCommands.Count - 1; i >= 0; i--)
            {
                var cmd = mCommands[i];
                cmd.isComplete = true;
                cmd.OnBreak();
            }
            for (int i = mSubCmdQueue.Count - 1; i >= 0; i--)
            {
                var cmd = mSubCmdQueue[i];
                if (!cmd.isComplete)
                {
                    cmd.isComplete = true;
                    cmd.OnBreak();
                }
            }
            for (int i = mMainCmdQueue.Count - 1; i >= 0; i--)
            {
                var cmd = mMainCmdQueue[i];
                if (!cmd.isComplete)
                {
                    cmd.isComplete = true;
                    cmd.OnBreak();
                }
            }
            for (int i = 0; i < mActiveWorkers; i++)
            {
                mWorkers[i].workerId = -1;
                var flag = mWorkers[i].threadFlag;
                if (flag != null)
                    flag.Abort();
            }
            mActiveWorkers = 0;
            mMainCmdQueue.Clear();
            mSubCmdQueue.Clear();
            mCommands.Clear();
            Debug.LogFormat("[Parallel] ParallelDispatcher was disposed.");
        }

        ~ParallelDispatcher()
        {
            Dispose();
        }

        ThreadWorker StartWorker()
        {
            if (mActiveWorkers < mWorkers.Length)
            {
                var worker = mWorkers[mActiveWorkers];
                worker.workerId = mActiveWorkers;
                if (worker.threadFlag == null || worker.threadFlag.IsAbort)
                {
                    worker.threadFlag = new ThreadFlag();
                    worker.threadFlag.Start(worker.exectutor);
                }
                if (worker.threadFlag.IsAbort)
                {
                    worker.workerId = -1;
                    return null;
                }
                mActiveWorkers++;
                return worker;
            }
            return null;
        }

        void StartLooperThread()
        {
            if (mLooper.threadFlag == null || mLooper.threadFlag.IsAbort)
            {
                mLooper.threadFlag = new ThreadFlag();
                mLooper.threadFlag.Start(mLooper.executor);
            }
        }

        void StopWorker(ThreadWorker worker)
        {
            if (worker.workerId == -1)
                return;
            var last = mActiveWorkers - 1;
            if (last != worker.workerId)
            {
                var lastworker = mWorkers[last];
                mWorkers[worker.workerId] = lastworker;
                lastworker.workerId = worker.workerId;
                mWorkers[last] = worker;
            }
            worker.workerId = -1;
            if (worker.threadFlag != null)
                worker.threadFlag.Abort();
            mActiveWorkers--;
        }

        public string GetCommandsInfo()
        {
            var buf = ParallelUtils.GetBuilder();
            MinHeap<BaseCommand> cmds = new MinHeap<BaseCommand>(mCommands.Count + mSubCmdQueue.Count + mMainCmdQueue.Count,
                (a, b) =>
                {
                    if (a.IsReady)
                        return -1;
                    if (b.IsReady)
                        return 1;
                    if (IsDependentOn(a, b))
                        return 1;
                    return -11;
                });
            string[] states = { "## STANDBY SEQUENCE ##", "## EXECUTING ##", "## COMPLETE ##" };
            int stat = -1;
            lock (mLock)
            {
                mMainThreadWorker.GetExecutingCmds(cmds);
                mLooper.GetExecutingCmds(cmds);
                for (int i = 0; i < mActiveWorkers; i++)
                {
                    mWorkers[i].GetExecutingCmds(cmds);
                }
                for (int i = 0; i < mMainCmdQueue.Count; i++)
                {
                    cmds.Add(mMainCmdQueue[i]);
                }
                for (int i = 0; i < mSubCmdQueue.Count; i++)
                {
                    cmds.Add(mSubCmdQueue[i]);
                }
                for (int i = 0; i < mCommands.Count; i++)
                {
                    cmds.Add(mCommands[i]);
                }
                while (cmds.Count > 0)
                {
                    var cmd = cmds.RemoveTop();
                    var exec = cmd.isComplete ? 2 : (cmd.IsReady ? 1 : 0);
                    if (exec != stat)
                    {
                        stat = exec;
                        buf.Append(states[exec]).NextLine();
                    }
                    buf.Append('<').Append(cmd.id).Append("> \"").Append(cmd.ToString()).Append('\"');
                    if (stat == 0 && cmd.dependentOn != null)
                    {
                        buf.Append("    [dependent on: ").Append(cmd.dependentOn.id);
                        if (cmd is WaitCmd)
                        {
                            var lst = ((WaitCmd)cmd).mWaitTargets;
                            if (lst != null)
                            {
                                for (int i = 0; i < lst.Count; i++)
                                {
                                    var prev = lst[i];
                                    if (!prev.isComplete)
                                        buf.Append(" ,").Append(prev.id);
                                }
                            }
                        }
                        buf.Append("]").NextLine();
                    }
                    else if (stat == 1)
                    {
                        float progress = 0;
                        int num = 0;
                        GetDependentExecuteProgress(cmd, ref progress, ref num);
                        buf.Append("    (").Append(num > 0 ? (progress / num).ToString("0.##%") : "0%").Append(")").NextLine();
                    }
                    buf.NextLine();
                }
            }
            return ParallelUtils.ReturnRelease(buf);
        }

        internal abstract class Worker : IThreadWorker
        {
            internal readonly ParallelDispatcher schedule;
            protected BaseCommand current;
            protected CascadeList<BaseCommand> mWaitFlags;

            internal Worker(ParallelDispatcher schedule)
            {
                this.schedule = schedule;
                mWaitFlags = new CascadeList<BaseCommand>(16, (a, b) => a.id == b.id);
            }

            internal void GetExecutingCmds(ICollection<BaseCommand> col)
            {
                var it = current;
                if (it != null)
                    col.Add(it);
                for (int i = mWaitFlags.Count - 1; i >= 0; i--)
                {
                    it = mWaitFlags[i];
                    if (it != null && it != current)
                        col.Add(it);
                }
            }

            internal int RemoveCmd(CascadeList<BaseCommand> lst, BaseCommand cmd, int starti)
            {
                for (int i = starti < lst.Count ? starti : -1; i >= 0; i--)
                {
                    if (lst[i] == cmd)
                    {
                        lst.RemoveAt(i);
                        return i;
                    }
                }
                for (int i = lst.Count - 1; i > starti; i--)
                {
                    if (lst[i] == cmd)
                    {
                        lst.RemoveAt(i);
                        return i;
                    }
                }
                return -1;
            }

        }

        internal class MainWorker : Worker, ILoopWorker
        {
            int mBatchNum;

            public float deltaTime { get; private set; }

            internal MainWorker(ParallelDispatcher schedule, int batchNum) : base(schedule)
            {
                mBatchNum = ParallelUtils.Max(32, batchNum);
            }

            internal void Update(float deltaTime)
            {
                this.deltaTime = deltaTime;
                for (int i = mWaitFlags.Count - 1; i >= 0; i--)
                {
                    current = mWaitFlags[i];
                    current.Execute(this);
                    if (current.isComplete)
                        mWaitFlags.RemoveAt(i);
                }
                int exec = 0;
                int execi;
                while (exec < mBatchNum)
                {
                    lock (schedule.mLock)
                    {
                        if (schedule.mMainCmdQueue.Count > 0)
                        {
                            execi = schedule.mMainCmdQueue.Count - 1;
                            current = schedule.mMainCmdQueue[execi];
                            schedule.mMainCmdQueue.RemoveAt(execi);
                        }
                        else
                        {
                            current = null;
                        }
                    }

                    if (current != null)
                    {
                        exec++;
                        current.Execute(this);
                        if (!current.isComplete)
                        {
                            mWaitFlags.Add(current);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        internal class ThreadWorker : Worker
        {
            public readonly int id;
            internal readonly WaitCallback exectutor;
            //internal int threadId;
            internal int workerId;
            internal ThreadFlag threadFlag;
            //int currentIndex;
            //readonly object _lock = new object();
            internal ThreadWorker(ParallelDispatcher schedule, int id) : base(schedule)
            {
                this.id = id;
                workerId = -1;
                exectutor = Execute;
                mWaitFlags = new CascadeList<BaseCommand>(16, (a, b) => a.id == b.id);
            }

            void Execute(object state)
            {
                //threadId = Thread.CurrentThread.ManagedThreadId;
                var flag = state as ThreadFlag;
                //int batch = 32;
                while (!flag.IsAbort)
                {
#if UNITY_EDITOR
                    if (sPause)
                    {
                        Thread.Sleep(TICK_MILLIS);
                        continue;
                    }
#endif

                    int exec = mWaitFlags.Count;
                    for (int i = mWaitFlags.Count - 1; i >= 0; i--)
                    {
                        current = mWaitFlags[i];
                        current.Execute(this);
                        if (current.isComplete)
                            mWaitFlags.RemoveAt(i);
                    }
                    if (exec > 8)
                        Thread.Yield();
                    int execi = 0;
                    lock (schedule.mLock)
                    {
                        if (schedule.mSubCmdQueue.Count > 0)
                        {
                            schedule.mExecIndex = (schedule.mExecIndex + 1) % schedule.mSubCmdQueue.Count;
                            execi = schedule.mExecIndex;
                            current = schedule.mSubCmdQueue[execi];
                            if (current.executeThread != ExecuteThread.Parallel)
                                schedule.mSubCmdQueue.RemoveAt(execi);
                            //current = schedule.mExecuteQueue.RemoveAt(schedule.mExecIndex);
                        }
                        else
                        {
                            current = null;
                        }
                    }

                    if (current != null)
                    {
                        exec++;
                        current.Execute(this);
                        if (!current.isComplete && current.executeThread != ExecuteThread.Parallel)
                        {
                            mWaitFlags.Add(current);
                        }
                        else if (current.isComplete && current.executeThread == ExecuteThread.Parallel)
                        {
                            lock (schedule.mLock)
                            {
                                RemoveCmd(schedule.mSubCmdQueue, current, execi);
                            }
                        }
                    }
                    if (exec > 0)
                    {
                        Thread.Yield();
                    }
                    else
                    {
                        Thread.Sleep(TICK_MILLIS);
                        lock (schedule.mLock)
                        {
                            if (schedule.mSubCmdQueue.Count == 0)
                                schedule.StopWorker(this);
                        }
                    }
                }
            }

        }

        internal class LoopWorker : Worker, ILoopWorker
        {
            public float deltaTime { get; private set; }
            internal ThreadFlag threadFlag;
            internal readonly WaitCallback executor; 
            public LoopWorker(ParallelDispatcher dispatcher) : base(dispatcher) 
            {
                executor = Execute;
            }

            internal void Join(BaseCommand cmd)
            {
                mWaitFlags.Add(cmd);
            }

            void Execute(object state)
            {
                var ticks = TICK_MILLIS * 10000;
                var flag = state as ThreadFlag;
                //var watch = new System.Diagnostics.Stopwatch();
                //watch.Start();
                long lastTick = System.DateTime.UtcNow.Ticks;// watch.ElapsedTicks;
                Thread.Sleep(TICK_MILLIS);
                //// fix 预测线程唤醒时间，以轻微修正
                //int fix = (int)(System.DateTime.Now.Ticks - lastTick - ticks);
                //if (fix < 0)
                //    fix = 0;
                while (flag != null && !flag.IsAbort)
                {
                    var t = System.DateTime.UtcNow.Ticks;
                    var delta = t - lastTick;
                    lastTick = t;
                    deltaTime = (float)(delta * tick_2_seconds);
                    int exec = 0;
#if UNITY_EDITOR
                    if (!sPause)
                    {
#endif
                        int index;
                        lock (schedule.mLock)
                        {
                            index = mWaitFlags.Count - 1;
                            current = index >= 0 ? mWaitFlags[index] : null;
                        }
                        while (index >= 0)
                        {
                            exec++;
                            if (current != null)
                            {
#if PROFILING
                                Profiler.BeginSample(current.ToString());
#endif
                                try
                                {
                                    current.Execute(this);
                                }
                                catch (System.Exception e)
                                {
                                    Debug.LogException(e);
                                }
#if PROFILING
                                Profiler.EndSample();
#endif
                            }
                            lock (schedule.mLock)
                            {
                                if (current != null && current.isComplete)
                                {
                                    index = RemoveCmd(mWaitFlags, current, index);
                                    current = null;
                                }
                                index = ParallelUtils.Min(index - 1, mWaitFlags.Count - 1);
                                current = index >= 0 ? mWaitFlags[index] : null;
                            }
                        }
#if UNITY_EDITOR
                    }
#endif
                    current = null;
                    t = System.DateTime.UtcNow.Ticks;
                    var sleep = (int)(lastTick + ticks + ticks - delta - t);
                    if (sleep > 10)
                        Thread.Sleep(sleep / 10000);
                    else
                        Thread.Yield();
                    if (exec == 0)
                    {
                        lock (schedule.mLock)
                        {
                            if (mWaitFlags.Count == 0)
                                flag.Abort();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 多线程任务执行句柄，用以查询任务执行状态
        /// </summary>
        public struct Handler : ICommandHandler, System.IEquatable<Handler>
        {
            internal readonly BaseCommand cmd;
            internal readonly ParallelDispatcher schedule;

            internal Handler(ParallelDispatcher schedule, BaseCommand cmd)
            {
                this.cmd = cmd;
                this.schedule = schedule;
            }

            public bool Equals(Handler other)
            {
                return cmd == other.cmd && schedule == other.schedule;
            }

            internal bool IsDependentOn(BaseCommand dependent)
            {
                if (schedule == null || cmd == null || cmd.isComplete)
                    return false;
                lock (schedule.mLock)
                {
                    return schedule.IsDependentOn(cmd, dependent);
                }
            }

            internal int GetExecutableId(ParallelDispatcher threading)
            {
                if (threading == schedule && cmd != null && !cmd.isComplete)
                    return cmd.id;
                else
                    return 0;
            }

            /// <summary>
            /// 任务是否为可执行状态
            /// </summary>
            public bool IsValid { get { return cmd != null && !cmd.isComplete && !schedule.mDisposed; } }

            /// <summary>
            /// 任务对象是否与给定的对象相同
            /// </summary>
            /// <param name="command"></param>
            /// <returns></returns>
            public bool IsCommand(object command)
            {
                return this.cmd == null ? command == null : cmd.IsCommand(command);
            }

            /// <summary>
            /// 中断任务执行
            /// </summary>
            /// <param name="includeDependents"></param>
            public void Abort(bool includeDependents = false)
            {
                if (schedule != null)
                {
                    schedule.Abort(cmd, includeDependents);
                }
            }

            /// <summary>
            /// 获取任务详细执行状态
            /// </summary>
            /// <returns></returns>
            public Execution GetExecution()
            {
                Execution exec;
                if (cmd == null || schedule == null || cmd.isComplete)
                {
                    exec.state = EExecutionState.Complete;
                    exec.progress = 1f;
                }
                else
                {
                    exec.progress = 0;
                    lock (schedule.mLock)
                    {
                        exec.state = cmd.IsReady ? EExecutionState.Executing : EExecutionState.Standby;
                        if (exec.state == EExecutionState.Executing)
                        {
                            float progress = 0;
                            int num = 0;
                            schedule.GetDependentExecuteProgress(cmd, ref progress, ref num);
                            exec.progress = num > 0 ? (progress / (float)num) : 0;
                        }
                    }
                }
                return exec;
            }

            /// <summary>
            /// 等待任务执行完成，该方法会阻塞线程，请谨慎使用（为了游戏体验，该方法不能被主线程调用），
            /// 如非必要，应当使用任务依赖系统或 Yield Command 系统来实现类似的功能
            /// </summary>
            /// <exception cref="System.Exception"></exception>
            public void WaitComplete()
            {
                if (cmd == null || schedule == null || cmd.isComplete)
                    return;

                if (schedule.mMainThreadID == Thread.CurrentThread.ManagedThreadId)
                    throw new System.Exception("Parallel.Wait don't allowed to be invoked on main thread.");

#if PROFILING
                Profiler.BeginSample(cmd.ToString());
#endif
                while (!cmd.isComplete)
                {
                    Thread.Sleep(TICK_MILLIS);
                }
#if PROFILING
                Profiler.EndSample();
#endif
            }

            public Handler GetHandler()
            {
                return this;
            }

            public void SampleExecuteTime()
            {
#if PROFILING
                if (cmd != null)
                    cmd.BeginSample();
                else
                    Debug.LogWarning("invalid parallel handler.");
#endif
            }

            public override string ToString()
            {
                return cmd == null ? "invaild cmd" : cmd.ToString();
            }

        }

        /// <summary>
        /// 组合任务依赖句柄
        /// </summary>
        public struct CombinedHandler : ICommandHandler
        {
            string info;
            WaitCmd cmd;
            readonly ParallelDispatcher schedule;
            public bool IsValid { get { return cmd != null && !cmd.isComplete; } }
            public bool IsNull { get { return schedule == null; } }

            public CombinedHandler(string info)
            {
                lock (sync_lock)
                {
                    if (sSchedule == null)
                        throw new System.Exception("ParallelDispatcher was not initialized.");
                    schedule = sSchedule;
                }
                this.info = info;
                this.cmd = null;
            }

            public Handler GetHandler()
            {
                return new Handler(schedule, cmd);
            }

            public void AbortAll(bool includeAllDependent = false)
            {
                if (schedule != null)
                {
                    if (includeAllDependent)
                    {
                        schedule.Abort(cmd, true);
                    }
                    else if (cmd != null && !cmd.isComplete)
                    {
                        var set = new HashSet<BaseCommand>();
                        cmd.GetAllJoinedCommands(set);
                        foreach (var job in set)
                        {
                            schedule.Abort(job, false);
                        }
                    }
                }
            }

            public void Combine(Handler handle)
            {
                if (schedule == null)
                {
                    throw new System.Exception("Combined Handler was not initialized (combine parallel cmds).");
                }
                var id = cmd == null || cmd.isComplete ? 0 : cmd.id;
                var cmdId = handle.GetExecutableId(schedule);
                if (cmdId == 0 || id == cmdId)
                    return;
                lock (schedule.mLock)
                {
                    if (id != 0 && schedule.IsDependentOn(cmd, handle.cmd))
                        return;
                    if (id != 0 && schedule.IsDependentOn(handle.cmd, cmd))
                        throw new System.Exception(string.Format("合并命令({0})已经依赖于被合并目标({1}),这将导致死锁", handle.cmd, cmd));
                    if (cmd != null && !cmd.isComplete)
                    {
                        cmd.Join(handle.cmd);
                    }
                    else
                    {
                        id = schedule.NextID();
                        cmd = new WaitCmd(id, handle.cmd);
#if PROFILING
                        cmd.SetInfo(info);
#endif
                        schedule.mCommands.Add(cmd);
                    }
                }
            }

            public override string ToString()
            {
                if (string.IsNullOrEmpty(info))
                    return "combined parallel cmd(s)";
                else
                    return info;
            }
        }

    }
}
//       